package at.technikum.bic.ese.hatcam;

import java.io.IOException;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.hardware.Camera;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import at.technikum.bic.ese.hatcam.services.CameraShotService;
import at.technikum.bic.ese.hatcam.services.CameraShotService.CameraShotServiceBinder;
import at.technikum.bic.ese.hatcam.services.OnCameraShotServiceFinishedCallback;

/**
 * Die CameraPreviewActivity zeigt das Kamerabild an und erlaubt es einzelne Fotos aufzunehmen. 
 * Sie zeigt das Kamerabild an, bietet jedoch keine Funktionalit�t 
 * zum Schiessen von Bildern. 
 */
public class CameraPreviewActivity extends Activity implements SurfaceHolder.Callback, OnCameraShotServiceFinishedCallback {
	/* Anwendungs Log-Tag f�r das filtern mit Logcat */
	private static final String TAG = "Hatcam";
	/* Kamera-Ger�t zur Vorschau */
	private Camera camera = null; 
	/* Oberfl�che zum Anzeigen der Kamera */
	private SurfaceView surfaceView;
	/* Beh�lter zum Zeichnen des Kamerabildes */
	private SurfaceHolder surfaceHolder;
	/* Variable die angibt ob bereits eine Vorschau l�uft */
	private boolean previewing = false;
	/* Das CameraShotService sobald es an die Activity gebunden wurde */
	private CameraShotService mService = null;
	
	/** 
	 * Wird aufgerufen wenn die Activity das erste mal erstell wird.
	 * Hier wird die Anzeige-Oberfl�che f�r das Kamerabild initialisiert.
	 */
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		Log.i(TAG, "CameraPreviewActivity wurde erstellt (onCreate)");
		
		// Binde das entsprechende Layout an die Activity
		setContentView(R.layout.camerapreview);

		setupOneShotButton();
		setupPreviewSurface();
	}
	
	/**
	 * Bindet das CameraShotService an die Activity. Dies erm�glicht das �bergeben
	 * der SurfaceView, und somit eine gleichzetige Verwendung der View f�r die
	 * Aufnahme eines Fotos, und die Darstellung des Kamerabilds in der Activity.
	 */
	private void bindCameraShotService() {
		Log.i(TAG, "Beginne mit dem Binding an das CameraShotService (bindCameraShotService)");
		Intent i = new Intent(this, CameraShotService.class);
		startService(i);
		if(getApplicationContext().bindService(i, mConnection, Context.BIND_AUTO_CREATE) == false)
			Log.e(TAG, "Konnte das CameraShotService nicht binden");
	}

	/**
	 * Initialisiert das OnClick-Event des Buttons, welches dann �ber das gebundene
	 * CameraShotService eine Aufnahme startet.
	 */
	private void setupOneShotButton() {
		Button oneShotButton = (Button) findViewById(R.id.camera_oneshot_button);
		final Context ctx = this;
		oneShotButton.setOnClickListener(new OnClickListener() {
			
			public void onClick(View v) {
				Log.i(TAG, "Der Benutzer l�ste den Kamera One-Shot aus (oneShotButton.onClick)");
				
				mService.setOnCameraShotServiceFinishedCallback((OnCameraShotServiceFinishedCallback) ctx);
				
				// Falls das Intervall nicht l�uft, muss ACTION_ONESHOT �bergeben werden
				//i.setAction(CameraShotService.ACTION_ONESHOT);
				closeCamera();
				//showCameraBusyText();
				
				mService.takePicture();
			}
		});
		
	}
	/**
	 * Wird aufgerufen sobald die Activity geschlossen wird, zum Beispiel
	 * weil auf eine andere Activity der Anwendung gewechselt wurde. Hier
	 * wird die Kameravorschau beendet, und das Kamera-Device wieder frei
	 * gegeben.
	 */
	@Override
	protected void onPause() {
		super.onPause();
		stopPreviewing();
		closeCamera();
	}

	/**
	 * Wird bei Start der Activity aufgerufen, sowohl beim ersten mal, als
	 * auch wenn die Activity zuvor pausiert wurde, und nun wieder ge�ffnet
	 * wurde. Hier wird das Kamera-Device ge�ffnet, und die Kameravorschau
	 * gestartet. 
	 */
	@Override
	protected void onResume() {
		Log.i(TAG, "CameraPreviewActivity wurde wiederaufgenommen (onResume)");
		super.onResume();
		bindCameraShotService();
		openCamera();
		startPreviewing();
	}
	
	/**
	 * Diese Methode initialisiert die Oberfl�che zum Zeichnen des
	 * Kamerabilds. Dabei setzt sie die {@link CameraPreviewActiviy}
	 * als {@link SurfaceHolder.Callback} f�r die Erstellung der
	 * Zeichenoberfl�che f�r das Kamerbild.
	 */
	private void setupPreviewSurface() {
		Log.i(TAG, "Beginne mit dem Setup der Vorschaufl�che (setupPreviewSurface)");
		// Lade das SurfaceView zum anzeigen des Kamerabilds
		surfaceView = (SurfaceView) findViewById(R.id.camera_preview_surface);

		// Hole die Zeichenfl�che f�r das Bild
		surfaceHolder = surfaceView.getHolder();
		
		// Setze einen Callback f�r Events der Zeichenoberfl�che (created, changed, destroyed)
		surfaceHolder.addCallback(this);
		
		// Angeblich deprecated. Geht aber nur mit.
		surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
		
		// �bernimm die Gr��e vom verwendeten Layout
		surfaceHolder.setSizeFromLayout();
	}

	/**
	 * Diese Methode �ffnet das Kamera-Device. Bei Misserfolg, etwa 
	 * wenn das Kamera-Device bereits besch�ftigt ist, zeigt es den
	 * Fehler-Text an.
	 */
	private void openCamera() {
		Log.i(TAG, "Beginne mit dem �ffnen des Kamera-Device (openCamera)");
		try {
			// �ffne das Kamera-Device
			camera = Camera.open();
			camera.setDisplayOrientation(90);
			Log.i(TAG, "Kamera-Device erfolgreich ge�ffnet");
			hideCameraBusyText();
		} catch (RuntimeException ex) {
			Log.i(TAG, "Konnte Kamera nicht �ffnen. Device Busy");
			showCameraBusyText();
		}
	}
	
	/**
	 * Diese Methode gibt das Kamera-Device f�r weitere Verwendung frei.
	 */
	private void closeCamera() {
		Log.i(TAG, "Beginne mit dem Freigeben des Kamera-Device (closeCamera)");
		// Stoppe die Kameravorschau
		stopPreviewing();
		
		if(camera != null) {
			// Gib Kamera-Device frei
			camera.release();
			camera = null;
		
			Log.i(TAG, "Kamera-Device wurde geschlossen");
		}
	}
	
	/**
	 * Diese Methode startet die Vorschau der Kamera auf der Zeichenoberfl�che.
	 */
	private void startPreviewing() {
		Log.i(TAG, "Beginne mit der Kameravorschau (startPreviewing)");
		if (!previewing && camera != null){
			try {
				// Binde die Kamera an die ge�nderte Zeichenoberfl�che
				camera.setPreviewDisplay(surfaceHolder);
				
				// Starte die Vorschau
				camera.startPreview();
				
				previewing = true;
				
			} catch (IOException e) {
				Log.i(TAG, "Konnte Kameravorschau nicht starten");
			}
		}
	}
	
	/**
	 * Diese Methode stoppt die Vorschau der Kamera.
	 */
	private void stopPreviewing() {
		Log.i(TAG, "Beende Kameravorschau (stopPreviewing)");
		if(previewing && camera != null) {
			camera.stopPreview();
			previewing = false;
		}
	}
	
	/**
	 * Diese Methode versteckt die Kamera-Oberfl�che und zeigt statt dessen
	 * den Fehlertext und ein Fehlerbild an.
	 */
	private void showCameraBusyText() {
		Log.i(TAG, "Zeige Text da Kamera Busy ist (showCameraBusyText)");
		surfaceView.setVisibility(View.GONE);
		
		Button oneShotButton = (Button) findViewById(R.id.camera_oneshot_button);
		oneShotButton.setVisibility(View.GONE);
		
		ImageView cameraBusyImage = (ImageView) findViewById(R.id.camera_preview_busy_image);
		cameraBusyImage.setVisibility(View.VISIBLE);
		
		TextView cameraBusyText = (TextView) findViewById(R.id.camera_preview_busy);
		cameraBusyText.setVisibility(View.VISIBLE);
	}
	
	/**
	 * Diese Methode versteckt den Fehlertext und das Fehlerbild und macht die
	 * Kamera-Oberfl�che wieder sichtbar.
	 */
	private void hideCameraBusyText() {
		Log.i(TAG, "Verstecke Busy-Text. Zeige Kamerabild (hideCameraBusyText)");
		surfaceView.setVisibility(View.VISIBLE);
		
		Button oneShotButton = (Button) findViewById(R.id.camera_oneshot_button);
		oneShotButton.setVisibility(View.VISIBLE);
		
		ImageView cameraBusyImage = (ImageView) findViewById(R.id.camera_preview_busy_image);
		cameraBusyImage.setVisibility(View.GONE);
		
		TextView cameraBusyText = (TextView) findViewById(R.id.camera_preview_busy);
		cameraBusyText.setVisibility(View.GONE);
	}
	
	/**
	 * Diese Callback-Routine wird aufgerufen sobald die Zeichenoberfl�che
	 * f�r die Kamera erstellt wurde, und verwendbar ist. F�r die 
	 * {@link CameraPreviewActivity} hat sie keine weitere Bedeutung.
	 */
	
	public void surfaceCreated(SurfaceHolder arg0) {
		Log.i(TAG, "Kamera-Oberfl�che wurde erstellt (surfaceCreated)");
	}

	/**
	 * Diese Callback-Routine wird aufgerufen sobald die Zeichenoberfl�che
	 * ver�ndert wurde (Ma�e, Sichtbarkeit, etc.). Hier wird die Kameravorschau
	 * gestartet, und eine m�gliche bereits laufende zuerst beendet.
	 */
	public void surfaceChanged(SurfaceHolder holder, int format, int width,	int height) {
		Log.i(TAG, "Kamera-Oberfl�che wurde ver�ndert (surfaceChanged)");
		stopPreviewing();
		startPreviewing();
	}

	/**
	 * Diese Callback-Routine wird aufgerufen sobald die Zeichenoberfl�che zerst�rt
	 * wurde, und gibt die Kamera wieder frei.
	 */
	public void surfaceDestroyed(SurfaceHolder arg0) {
		Log.i(TAG, "Kamera-Oberfl�che wurde zerst�rt (surfaceDestroyed)");
		closeCamera();
	}
	
	
	/**
	 * Die ServiceConnection welche das Binding mit dem CameraShotService
	 * �bernimmt. Wir beim Aufruf von bindService() verwendet.
	 */
	private ServiceConnection mConnection = new ServiceConnection() {

       
        public void onServiceConnected(ComponentName className, IBinder service) {
            Log.i(TAG, "CameraPreviewActivity hat sich an das CameraShotService gebunden (onServiceConnected)");
        	// We've bound to LocalService, cast the IBinder and get LocalService instance
            CameraShotServiceBinder binder = (CameraShotServiceBinder) service;
            mService = binder.getService();
            mService.setExternalSurfaceView(surfaceView);
        }

       
        public void onServiceDisconnected(ComponentName arg0) {
        	Log.i(TAG, "Binding zum CameraShotService wurde beendet (onServiceDisconnected)");
        }
    };

    /**
     * Eine Callback-Methode welche vom gebundenen CameraShotService aufgerufen wird.
     * Sobald das CameraShotService eine Aufnahme beendet hat, wird hier die
     * Kameravorschau wieder gestartet.
     */
	
	public void cameraShotServiceFinished() {
		Log.i(TAG, "CameraShotService meldet den Abschluss einer Operation (cameraShotServiceFinished)");
		mService.removeOnCameraShotServiceFinishedCallback();
		openCamera();
		startPreviewing();
	}
}
